﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using DevExpress.Xpf.Grid.TreeList;
using Microsoft.WindowsAPICodePack.Dialogs;
using Newtonsoft.Json;
using OfficeOpenXml;
using OfficeOpenXml.Style;
using static ShieldTunnel2.ExpertFormOptions;
using static ShieldTunnel2.ExcelHelper;

namespace ShieldTunnel2
{
	public partial class ExpertRiskEvalFormPanel : UserControl
	{
		private List<RiskEvent> _riskEvents;
		private const string TemplateExcelFileName = "专家调查表";
		private string[] _selectedExpertNames = null;
		private RiskEvent[] _selectedRiskEvents = null;

		private readonly string[] _expertNames =
		{
			"张杰", "杨坤", "沙溢", "李茂", "黄磊", "于小伟", "刘冠翔", "秦俊杰", "张琳",
			"陈坤", "黄觉", "邵峰", "陈旭", "马天宇", "杨子", "邓安奇", "赵鸿飞", "马可",
			"王睿", "保剑锋", "于震", "苏醒", "胡夏", "张丰毅", "刘翔", "李玉刚", "林依轮",
			"袁弘", "朱雨辰", "丁志诚", "黄征", "张子健", "许嵩", "向鼎",
		};

		public ExpertRiskEvalFormPanel()
		{
			InitializeComponent();
		}

		#region Control event handlers

		private async void ExpertRiskEvalFormPanel_OnLoaded(object sender, RoutedEventArgs e)
		{
			_riskEvents = await GetRiskEventsAsync();
			TreeListRiskEvents.ItemsSource = _riskEvents;

			ListBoxExperts.ItemsSource = _expertNames.Take(10).ToArray();
		}

		private async void ButtonGenerateExpertEvalForm_OnClick(object sender, RoutedEventArgs e)
		{
			_selectedExpertNames = ListBoxExperts.SelectedItems
				.Select(item => item.ToString())
				.ToArray();
			_selectedRiskEvents = _riskEvents.Where(re => re.IsSelected).ToArray();
			Debug.Assert(_selectedExpertNames != null);
			if (_selectedExpertNames.Length <= 0 || _selectedRiskEvents.Length <= 0)
				return;

			var ofd = new CommonOpenFileDialog()
			{
				IsFolderPicker = true,
			};
			if (ofd.ShowDialog() != CommonFileDialogResult.Ok)
				return;

			var fileFolderPath = ofd.FileName;
			Debug.Assert(Directory.Exists(fileFolderPath));
			var fileNames = Directory.GetFiles(fileFolderPath)
				.Where(fn => Path.GetFileNameWithoutExtension(fn)
					.StartsWith($"{TemplateExcelFileName}_"));
			foreach (var fileName in fileNames)
			{
				File.Delete(fileName);
			}

			await CreateExpertEvalFormAsync(fileFolderPath);

			MessageBox.Show("专家调查表创建完毕!", "", MessageBoxButton.OK, MessageBoxImage.Information);
		}

		private async Task CreateExpertEvalFormAsync(string fileFolderPath)
		{
			await Task.Run(() =>
			{
				var templateFilePath = Path.Combine(fileFolderPath,
					$"{TemplateExcelFileName}.xlsx");
				var fi = new FileInfo(templateFilePath);
				if (fi.Exists)
				{
					fi.Delete();
					fi = new FileInfo(templateFilePath);
				}

				using (var excelPackage = new ExcelPackage(fi))
				{
					var workbook = excelPackage.Workbook;

					//
					Debug.Assert(_selectedRiskEvents != null && _selectedRiskEvents.Length > 0);
					CreateExpertInfoForm(workbook);
					CreateRiskEventImportanceForm(workbook, _selectedRiskEvents);
					CreateRiskEventPossibilityForm(workbook, _selectedRiskEvents);
					CreateRiskEventLostForm(workbook, _selectedRiskEvents);
					CreateTotalLostImportanceForm(workbook);

					foreach (var expertName in _selectedExpertNames)
					{
						var destFilePath = Path.Combine(fileFolderPath,
							$"{TemplateExcelFileName}_{expertName}.xlsx");
						var destFileInfo = new FileInfo(destFilePath);
						if (destFileInfo.Exists)
						{
							destFileInfo.Delete();
							destFileInfo = new FileInfo(destFilePath);
						}
						excelPackage.SaveAs(destFileInfo);

						Debug.Assert(File.Exists(destFilePath));
					}
				}
			});
		}

		private void TreeListView_OnNodeCheckStateChanged(object sender, TreeListNodeEventArgs e)
		{
			var selectedRiskEvents = _riskEvents.Where(re => re.IsSelected);
			TreeListSelectedRiskEvents.ItemsSource = selectedRiskEvents;
		}

		private void ButtonExpandTreeListRiskEvents_OnClick(object sender, RoutedEventArgs e)
		{
			var buttonActions = new ValueTuple<Button, Action>[]
			{
				(ButtonExpandTreeListRiskEvents, TreeListRiskEvents.View.ExpandAllNodes),
				(ButtonCollapseTreeListRiskEvents, TreeListRiskEvents.View.CollapseAllNodes),
				(ButtonExpandTreeListSelectedRiskEvents, TreeListSelectedRiskEvents.View.ExpandAllNodes),
				(ButtonCollapseTreeListSelectedRiskEvents, TreeListSelectedRiskEvents.View.CollapseAllNodes),
			};
			Debug.Assert(buttonActions.Any(buttonAction => buttonAction.Item1 == sender));
			buttonActions.First(buttonAction => buttonAction.Item1 == sender).Item2.Invoke();
		}

		#endregion

		#region Create Excel Risk Evaluation Form

		/// <summary>
		/// 创建“专家个人信息”调查表
		/// </summary>
		/// <param name="workbook"></param>
		private void CreateExpertInfoForm(ExcelWorkbook workbook)
		{
			const string sheetName = "专家个人信息";
			var sheet = workbook.Worksheets.Add(sheetName);

			sheet.Column(1).Width = 35;
			sheet.Column(2).Width = 20;

			sheet.Cells[1, 1].Value = "专家个人信息";
			var range = sheet.Cells[1, 1, 1, 2];
			range.Merge = true;
			range.Style.Font.Bold = true;
			range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
			range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
			range.Style.Border.BorderAround(ExcelBorderStyle.Medium);
			sheet.Row(1).Height = 25;

			var formItems = new[]
			{
				("您所在的单位", ExpertDepartmentTypes.Select(o => o.Item1).ToArray()),
				("您的职称", ExpertProfessionalTitles.Select(o => o.Item1).ToArray()),
				("您的年龄", ExpertAgeSections.Select(o => o.Item1).ToArray()),
				("您从事隧道专业时间", ExpertWorkTimes.Select(o => o.Item1).ToArray()),
				("您对工程风险理论及方法的熟悉程度", ExpertRiskProfessionalLevels.Select(o => o.Item1).ToArray()),
				("您对地铁盾构隧道工程情况了解程度", ExpertShieldTunnelProfessionalLevels.Select(o => o.Item1).ToArray()),
			};
			for (var i = 0; i < formItems.Length; i++)
			{
				var row = i + 2;
				var formItem = formItems[i];
				sheet.Cells[row, 1].Value = formItem.Item1;
				AddValidationListToCell(sheet, row, 2, formItem.Item2);
			}

			range = sheet.Cells[2, 1, 1 + formItems.Length, 2];
			range.Style.Border.BorderAround(ExcelBorderStyle.Medium);
			range.Style.WrapText = true;
			range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;

			range = sheet.Cells[2, 2, 1 + formItems.Length, 2];
			range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
		}

		/// <summary>
		/// 创建“风险事件重要度”调查表
		/// </summary>
		/// <param name="workbook"></param>
		/// <param name="riskEvents"></param>
		private void CreateRiskEventImportanceForm(ExcelWorkbook workbook, IList<RiskEvent> riskEvents)
		{
			Debug.Assert(workbook != null && IsListNotEmpty(riskEvents));

			var maxLevel = riskEvents.Max(re => re.Level);
			if (maxLevel <= 1)
				return;

			const string sheetName = "风险事件重要度";
			var sheet = workbook.Worksheets.Add(sheetName);

			var levelText = new[] {"一", "二", "三", "四", "五"};
			var startCol = 1;
			for (var i = 0; i < maxLevel - 1; i++) // for each risk level
			{
				Debug.Assert(i < levelText.Length);

				//
				// Title
				//
				sheet.Cells[1, startCol].Value = $"{levelText[i]}级事件";
				sheet.Cells[1, startCol + 1].Value = $"{levelText[i + 1]}级事件";
				sheet.Cells[1, startCol + 2].Value = "重要程度";
				var range = sheet.Cells[1, startCol, 1, startCol + 2];
				range.Style.Font.Bold = true;
				range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
				range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
				range.Style.Border.BorderAround(ExcelBorderStyle.Medium);
				sheet.Row(1).Height = 25;

				sheet.Column(startCol).Width = 15;
				sheet.Column(startCol + 1).Width = 30;
				sheet.Column(startCol + 3).Width = 2;

				var higherLever = i + 1;
				var higherLevelEvents = riskEvents.Where(re =>
					re.Level == higherLever && IsListNotEmpty(re.ChildrenRiskEvents));
				var startRow = 2;
				foreach (var higherLevelEvent in higherLevelEvents) // for each risk event level
				{
					var childrenEvents = higherLevelEvent.ChildrenRiskEvents
						.Where(re => re.IsSelected)
						.ToList();

					var childrenEventCount = childrenEvents.Count;
					if (childrenEventCount <= 0)
						continue;

					//
					// Higer level event
					//
					sheet.Cells[startRow, startCol].Value = higherLevelEvent.FullName;
					range = sheet.Cells[startRow, startCol, startRow + childrenEventCount - 1, startCol];
					range.Merge = true;

					//
					// Lower level events (children events)
					//
					var j = 0;
					foreach (var childEvent in childrenEvents) // for each child event
					{
						Debug.Assert(childEvent.IsSelected);
						sheet.Cells[startRow + j, startCol + 1].Value = childEvent.FullName;
						j++;
					}
					AddValidationListToRange(sheet, startRow, startCol + 2,
						startRow + childrenEventCount - 1, startCol + 2, ImportanceLevels);

					range = sheet.Cells[startRow, startCol, startRow + childrenEventCount - 1, startCol + 2];
					range.Style.Border.BorderAround(ExcelBorderStyle.Medium);
					range.Style.WrapText = true;
					range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;

					range = sheet.Cells[startRow, startCol + 2, startRow + childrenEventCount - 1, startCol + 2];
					range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;

					//
					startRow += childrenEventCount;
				}

				const int colSkipCount = 4;
				startCol += colSkipCount;
			}
		}

		/// <summary>
		/// 创建“风险事件发生概率”调查表
		/// </summary>
		/// <param name="workbook"></param>
		/// <param name="riskEvents"></param>
		private void CreateRiskEventPossibilityForm(ExcelWorkbook workbook, IList<RiskEvent> riskEvents)
		{
			Debug.Assert(workbook != null && IsListNotEmpty(riskEvents));

			var primaryEvents = riskEvents
				.Where(re => re.IsPrimaryEvent)
				.ToList();
			Debug.Assert(primaryEvents.Count > 0 && primaryEvents.Min(re => re.Level >= 2));

			const string sheetName = "风险事件发生概率";
			var sheet = workbook.Worksheets.Add(sheetName);

			sheet.Column(1).Width = 35;

			sheet.Cells[1, 1].Value = "风险因素";
			sheet.Cells[1, 2].Value = "发生概率";
			sheet.Row(1).Height = 25;
			var range = sheet.Cells[1, 1, 1, 2];
			range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
			range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
			range.Style.Border.BorderAround(ExcelBorderStyle.Medium);
			range.Style.Font.Bold = true;

			var row = 2;
			foreach (var riskEvent in primaryEvents)
			{
				sheet.Cells[row, 1].Value = riskEvent.FullName;
				row++;
			}
			var options = new[] {"很小", "小", "较小", "中等", "较大", "大", "很大"};
			AddValidationListToRange(sheet, 2, 2, 1 + primaryEvents.Count, 2, options);

			range = sheet.Cells[2, 1, 1 + primaryEvents.Count, 2];
			range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
			range.Style.Border.BorderAround(ExcelBorderStyle.Medium);
			range.Style.WrapText = true;

			range = sheet.Cells[2, 2, 1 + primaryEvents.Count, 2];
			range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
		}

		/// <summary>
		/// 创建“风险事件损失”调查表
		/// </summary>
		/// <param name="workbook"></param>
		/// <param name="riskEvents"></param>
		private void CreateRiskEventLostForm(ExcelWorkbook workbook, IList<RiskEvent> riskEvents)
		{
			Debug.Assert(workbook != null && IsListNotEmpty(riskEvents));

			var primaryEvents = riskEvents
				.Where(re => re.IsPrimaryEvent)
				.ToList();
			Debug.Assert(primaryEvents.Count > 0 && primaryEvents.Min(re => re.Level >= 2));

			const string sheetName = "风险事件损失";
			var sheet = workbook.Worksheets.Add(sheetName);

			sheet.Column(1).Width = 35;

			sheet.Cells[1, 1].Value = "风险因素";
			sheet.Cells[1, 2].Value = "人员伤亡损失";
			sheet.Cells[1, 3].Value = "经济损失";
			sheet.Cells[1, 4].Value = "工期延误";
			sheet.Cells[1, 5].Value = "结构影响";
			sheet.Row(1).Height = 25;
			var range = sheet.Cells[1, 1, 1, 5];
			range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
			range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
			range.Style.Border.BorderAround(ExcelBorderStyle.Medium);
			range.Style.Font.Bold = true;

			var row = 2;
			foreach (var riskEvent in primaryEvents)
			{
				sheet.Cells[row, 1].Value = riskEvent.FullName;
				row++;
			}
			range = sheet.Cells[2, 1, 1 + primaryEvents.Count, 1];
			range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
			range.Style.Border.BorderAround(ExcelBorderStyle.Thin);
			range.Style.WrapText = true;

			var options = new[] {"轻微", "不大", "较大", "严重", "惨重"};
			AddValidationListToRange(sheet, 2, 2, 1 + primaryEvents.Count, 5, options);
			range = sheet.Cells[2, 2, 1 + primaryEvents.Count, 5];
			range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;

			range = sheet.Cells[2, 1, 1 + primaryEvents.Count, 5];
			range.Style.Border.BorderAround(ExcelBorderStyle.Medium);

			range = sheet.Cells[2, 2, 1 + primaryEvents.Count, 2];
			range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
		}

		/// <summary>
		/// 创建“总损失重要度”调查表
		/// </summary>
		/// <param name="workbook"></param>
		private void CreateTotalLostImportanceForm(ExcelWorkbook workbook)
		{
			Debug.Assert(workbook != null);

			const string sheetName = "总损失重要度";
			var sheet = workbook.Worksheets.Add(sheetName);

			sheet.Cells[1, 1].Value = "一级损失";
			sheet.Cells[1, 2].Value = "二级损失";
			sheet.Cells[1, 3].Value = "重要程度";
			var range = sheet.Cells[1, 1, 1, 3];
			range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
			range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
			range.Style.Border.BorderAround(ExcelBorderStyle.Medium);
			range.Style.Font.Bold = true;

			sheet.Cells[2, 1].Value = "总损失";
			range = sheet.Cells[2, 1, 5, 1];
			range.Merge = true;
			range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;

			sheet.Cells[2, 2].Value = "人员伤亡";
			sheet.Cells[3, 2].Value = "经济损失";
			sheet.Cells[4, 2].Value = "工期延误";
			sheet.Cells[5, 2].Value = "结构影响";
			range = sheet.Cells[2, 2, 5, 2];
			range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;

			AddValidationListToRange(sheet, 2, 3, 5, 3, ImportanceLevels);

			range = sheet.Cells[1, 1, 5, 3];
			range.Style.Border.BorderAround(ExcelBorderStyle.Medium);

			range = sheet.Cells[2, 3, 5, 3];
			range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
		}

		#endregion

		#region Helper methods

		private async Task<List<RiskEvent>> GetRiskEventsAsync()
		{
			RiskEvent GetChildRiskEvent(PotentialRiskEvent pre, List<RiskEvent> res,
				ref int nextId, int parentId, int childUid)
			{
				Debug.Assert(pre != null && res != null);
				var childRiskEvent = new RiskEvent()
				{
					Name = pre.Name,
					Level = pre.Level,
					Id = nextId,
					ParentId = parentId,
					Uid = childUid,
				};

				if (IsListNotEmpty(pre.ChildRiskEvents))
				{
					const int maxChildEventCount = 100;
					Debug.Assert(pre.ChildRiskEvents.Count < maxChildEventCount);

					var childrenEvents = new List<RiskEvent>();
					for (var i = 0; i < pre.ChildRiskEvents.Count; i++)
					{
						nextId++;
						var ci = childRiskEvent.Uid * 100 + i + 1;
						var re = GetChildRiskEvent(pre.ChildRiskEvents[i], res, ref nextId, childRiskEvent.Id, ci);
						childrenEvents.Add(re);
					}
					childRiskEvent.ChildrenRiskEvents = childrenEvents;
				}

				res.Add(childRiskEvent);
				return childRiskEvent;
			}

			return await Task.Run(() =>
			{
				const string filePath = @"AppData\PotentialRiskEvent.json";
				var text = File.ReadAllText(filePath);
				var rootEvent = JsonConvert.DeserializeObject<PotentialRiskEvent>(text);
				var riskEvents = new List<RiskEvent>();
				var startId = 1;
				GetChildRiskEvent(rootEvent, riskEvents, ref startId, -1, 1);

				return riskEvents;
			});
		}

		#endregion
	}
}
